package cnn.user.service;

import cnn.user.entity.UserDTO;
import cnn.sys.RestSession;
import cnn.sys.RestSessionHandler;
import cnn.topfox.misc.Misc;
import cnn.topfox.util.KeyBuild;
import cnn.user.dao.UserDao;
import cnn.user.entity.UserLoginQTO;
import cnn.topfox.common.CommonException;
import cnn.topfox.common.Response;
import cnn.topfox.common.ResponseCode;
import cnn.topfox.data.DbState;
import cnn.topfox.service.AdvancedService;

import cnn.user.entity.UserQTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class UserService extends AdvancedService<UserDao, UserDTO, UserQTO> {

    @Autowired
    RestSessionHandler restSessionHandler;

    @Override
    public void init() {
        //sysConfig.setThreadCache(true); //打开一级缓存
        sysConfig().setRedisCache(true);   //打开二级缓存

        sysConfig().setUpdateNotResultError(false); //根据Id更新结果为0不抛出异常

        //设置为 2小时 后过期(单位默认是小时)
        sysConfig().setRedisExpire(2);


        //设置为8分钟后过期-方法一
        sysConfig().setRedisExpire(8);
        sysConfig().setTimeUnit(TimeUnit.MINUTES);

        //设置为8分钟后过期-方法二
        sysConfig().setRedisCacheExpire(8,TimeUnit.MINUTES);
    }

    /**
     * 支持一次提交存在增删改的多行,依据DTO中getState()状态来识别增删改操作.
     * @see DbState
     * @param list
     */
    @Override
    public List<UserDTO> save(List<UserDTO> list) {
        // 执行增删改前的逻辑可以写在这里
        return super.save(list);

        // 执行增删改后的逻辑可以写在这里
    }

    /**
     * 多行执行本方法一次, 新增和修改的 之前的逻辑写到这里,  如通用的检查, 计算值的处理
     */
    @Override
    public void beforeInsertOrUpdate(List<UserDTO> list) {
        //多行执行依据SQL, 检查用户将编号是否重复 并抛出异常
        checkData(list)
                .addField("code", "用户编号")//有异常抛出的错误信息的字段中文标题自定义
                .excute();

        //多行执行依据SQL, 检查手机号是否重复 并抛出异常
        checkData(list)
                .addField("mobile", "手机号")//有异常抛出的错误信息的字段中文标题自定义
                .setWhere(where().ne("mobile","*")) //手机号为*的值不参与检查
                .excute();
    }


//    @Override
//    public void beforeInsertOrUpdate(UserDTO userDTO, String state) {
//        //这样每行新增修改的记录都会执行SQL
//        checkData(userDTO)
//                .addField("code", "用户编号")//有异常抛出的错误信息的字段中文标题自定义
//                .excute();
//        //多行执行依据SQL, 检查手机号是否重复 并抛出异常
//        checkData(userDTO)
//                .addField("mobile", "手机号")//有异常抛出的错误信息的字段中文标题自定义
//                .setWhere(where().ne("mobile","*")) //手机号为*的值不参与检查
//                .excute();
//    }

    @Override
    public void afterSave(List<UserDTO> list) {
        UserDTO temp = (UserDTO)attributes().get("dto");
        super.afterSave(list);
    }

    /**
     * 以下方法afterInsertOrUpdate 可以取消注释, 运行查看效果
     */
//    @Override
//    public void afterInsertOrUpdate(UserDTO userDTO, String state) {
//        UserDTO temp = (UserDTO)attributes().get("dto");
//
//        if (DbState.INSERT.equals(state)){
//            /**
//             * 写自己的业务逻辑
//             */
//        }else if (DbState.UPDATE.equals(state)){
//            /**
//             * 写自己的业务逻辑
//             */
//
//            if (false){
//                //虽然已经执行了SQL, 但抛出异常依然可以 回滚
//                throw CommonException.newInstance(ResponseCode.DATA_IS_INVALID).text("事务回滚测试");
//            }
//
//            /**
//             * 例一:获得默认修改日志信息
//             */
//            String modifyInfo = changeManager(userDTO)
//                    .addFieldLabel("name","用户姓名")
//                    .addFieldLabel("sex","性别")
//                    .output().toString();
//            logger.debug("修改日志:{}", modifyInfo);//打印默认的修改日志
//
//            //获得 JSON格式 日志信息
//            modifyInfo = changeManager(userDTO).outJSONString();
//            logger.debug("修改日志:{}", modifyInfo);//打印默认的修改日志
//        }
//    }

//    @Override
//    public void afterInsertOrUpdate(UserDTO userDTO, String state) {
//        if (DbState.UPDATE.equals(state)) {
//            // 例一:
//            ChangeManager changeManager = changeManager(userDTO)
//                    .addFieldLabel("name", "用户姓名")  //设置该字段的日志输出的中文名
//                    .addFieldLabel("mobile", "手机号"); //设置该字段的日志输出的中文名
//
//            //输出 方式一 参数格式
//            logger.debug("修改日志:{}", changeManager.output().toString() );
//            // 输出样例:
//            /**
//             修改日志:
//             id:000000,      //用户的id
//             用户姓名:开发者->开发者2,
//             手机号:13588330001->1805816881122
//             */
//
//            // 输出 方式二 JSON格式
//            logger.debug("修改日志:{}", changeManager.outJSONString() );
//            // 输出样例:  c是 current的简写, 是当前值, 新值; o是 old的简写, 修改之前的值
//            /**
//             修改日志:
//             {
//             "appName":"sec",
//             "executeId":"1561367017351_14",
//             "id":"000000",
//             "data":{
//             "version":{"c":"207","o":206},
//             "用户姓名":{"c":"开发者2","o":"开发者"},
//             "手机号":{"c":"1805816881122","o":"13588330001"}
//             }
//             }
//             */
//
//            //************************************************************************************
//            // 例二  没有用 addFieldLabel 设置字段输出的中文名, 则data中的keys输出全部为英文
//            logger.debug("修改日志:{}", changeManager(userDTO).outJSONString() );
//            // 输出 JSON格式
//            /**
//             修改日志:
//             {
//             "appName":"sec",
//             "executeId":"1561367017351_14",
//             "id":"000000",
//             "data":{
//             "version":{"c":"207","o":206},
//             "name":{"c":"开发者2","o":"开发者"},
//             "mobile":{"c":"1805816881122","o":"13588330001"}
//             }
//             }
//             */
//            //************************************************************************************
//        }
//    }

    /**
     * 选用事件, 这几个方法都会执行到: delete() deleteByIds() deleteBatch() deleteList()
     * @param beanDTO
     */
    @Override
    public void beforeDelete(UserDTO beanDTO) {
    }
    @Override
    public void beforeDelete(List<UserDTO> list) {
        /**
         * 多行删除,本方法在删除之"前"执行一次.没有特殊需求,别用
         */
    }
    @Override
    public void afterDelete(UserDTO beanDTO) {
    }
    @Override
    public void afterDelete(List<UserDTO> list) {
        /**
         * 多行删除,本方法在删除之"后"执行一次.没有特殊需求,别用
         */
    }


    @Transactional
    public Response<RestSession> login(UserLoginQTO userQTO) {
        //检查 用户账号 不能为空
        Misc.checkObjNotNull(userQTO.getUserId(), "用户账号");
        //检查 用户密码 不能为空
        Misc.checkObjNotNull(userQTO.getPassword(), "用户密码");

        UserDTO userDTO = baseMapper.login(userQTO);
        if (userDTO == null) {
            throw CommonException.newInstance(ResponseCode.DATA_IS_INVALID).text("用户账号" + userQTO.getUserId() + "不存在");
        }

        String password = Misc.gunzip(userQTO.getPassword(), "UTF-8");//密码解码(前台用gzip转成的压缩后的字符串)
        if (!userDTO.getPassword().equals(password)) {
            throw CommonException.newInstance(ResponseCode.DATA_IS_INVALID).text("密码不正确,请重试");
        }

        //给 restSession 写入必要的信息
        RestSession restSession = restSessionHandler.get();
        restSession.setSessionId(KeyBuild.getKeyId());

        //保存RestSession
        restSessionHandler.save(restSession);

        Response<RestSession> response = new Response(restSession);

        //线程安全的, 临时放入自定义的数据放回到调用方
        response.attributes().put("sessionId", restSession.getSessionId());

        return response;
    }



    public void test() {
        //给 restSession 写入必要的信息
        RestSession restSession = restSessionHandler.get();
        log.debug("createThreadId {} restSession:{}",restSession.getCreateThreadId(),restSession.hashCode());
    }
}
